package net.zoneland.zrdp.biz.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Preconditions;
import com.richercloud.lift.util.function.enhancer.BiFunctionEnhancer;
import com.richercloud.lift.util.function.enhancer.FunctionEnhancer;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import net.zoneland.zrdp.biz.common.Constants;
import net.zoneland.zrdp.biz.domain.IndicatorData;
import net.zoneland.zrdp.biz.domain.pojo.vo.IndicatorExcelDTO;
import net.zoneland.zrdp.biz.enums.IndicatorCityEnum;
import net.zoneland.zrdp.biz.mapper.IndicatorDataMapper;
import net.zoneland.zrdp.biz.service.IIndicatorDataService;


/**
 * 指标库数据Service业务层处理.
 * 创建日期: 2024-03-16
 * @author helu
 */
@Service
public class IndicatorDataServiceImpl implements IIndicatorDataService {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndicatorDataServiceImpl.class);;
    /** 指标excel表格的实际数据起始行. **/
    private static final int EXCEL_DATA_START_ROW = 5;
    /** 百分号. */
    private static final String PERCENT_SIGN = "%";
    /** 特殊百分号. */
    private static final String PERCENT_SIGN_SPECIAL = "％";
    /** 百分号集合. */
    private static final List<String> PERCENT_SIGNS = Arrays.asList(PERCENT_SIGN, PERCENT_SIGN_SPECIAL);
    /** 百分数值起始数据. */
    private static final String PERCENT_START_VALUE = "0.";
    /** 默认更新数量. */
    private static final int DEFAULT_UPDATE_NUMBER = 0;
    /** 指标名称参数. */
    private static final String INDEX_NAME = "indexName";
    /** excel模版错误提示. */
    private static final String EXCEL_ERROR = "导入模板错误！";
    /** 每行数据导入失败提示. */
    private static final String ROW_ERROR_MSG = "<br/>第%d行,”%s“导入失败：";
    /** * 指标单位参数.  */
    private static final String INDEX_UNIT = "indexUnit";
    /** 保留两位小数格式. */
    private static final String TWO_DECIMAL_FORMAT = "#.##";
    /** 保留小数位数. */
    private static final int DECIMALS = 2;
    /** 指标值必须为数字的指标. */
    private static final List<String> NUMBER_VALUE_INDEX_NAME = Arrays.asList("通信服务收入",
        "利润总额", "收入利润率", "客户满意度", "收入市场份额", "通信用户市场份额", "5G用户市场份额",
        "手机上网流量市场份额", "家宽客户市场份额", "千兆宽带用户市场份额", "政企市场收入市场份额",
        "员工满意度", "人均劳动生产率", "人均劳动利用率");

    @Resource
    private IndicatorDataMapper indicatorDataMapper;

    /**
     * 查询指标库数据列表.
     * @param indicatorData 指标库数据
     * @return 指标库数据
     */
    @Override
    public List<IndicatorData> selectIndicatorDataList(final IndicatorData indicatorData) {
        return indicatorDataMapper.selectIndicatorDataList(indicatorData);
    }

    /**
     * 批量插入指标数据.
     * @param indicatorList 指标数据
     * @return 结果
     */
    public int insertBatchIndicatorData(final List<IndicatorData> indicatorList) {
        return CollectionUtils.isNotEmpty(indicatorList)
            ? indicatorDataMapper.insertBatchIndicatorData(indicatorList) : DEFAULT_UPDATE_NUMBER;
    }

    /**
     * 导入指标数据.
     * @param dataList 指标数据
     * @return 导入结果
     * @author helu
     * @Date 17:52 2024/3/12
     */
    @Override
    @Transactional
    public String importIndicator(final List<IndicatorExcelDTO> dataList) {
        Preconditions.checkArgument(CollectionUtils.isNotEmpty(dataList), EXCEL_ERROR);

        //map记录最新行的指标名称和单位
        final Map<String, String> indicatorMap = new HashMap<>();
        indicatorMap.put(INDEX_NAME, StringUtils.EMPTY);
        indicatorMap.put(INDEX_UNIT, StringUtils.EMPTY);

        final StringBuilder failureMsg = new StringBuilder();

        //遍历导入对象，导入指标数据
        final List<IndicatorData> indicatorList = new ArrayList<>();
        final ListIterator<IndicatorExcelDTO> excelListIterator = dataList.listIterator();
        while (excelListIterator.hasNext()) { //需要获取下标，所以没用stream
            indicatorList.addAll(getRowIndicators(indicatorMap, excelListIterator, failureMsg));
        }

        //如有错误信息就抛出异常
        final String errorMsg = failureMsg.toString();
        Preconditions.checkArgument(StringUtils.isBlank(errorMsg), errorMsg);

        //批量导入指标数据
        insertBatchIndicatorData(indicatorList);
        return errorMsg;
    }

    /**
     * 获取一行excel数据中的指标数据.
     *
     * @param indicatorMap      指标参数
     * @param excelListIterator excel数据
     * @param failureMsg        错误信息
     * @return java.util.List<net.zoneland.zrdp.biz.domain.IndicatorData>
     * @author helu
     * @Date 14:55 2024/5/23
     */
    public List<IndicatorData> getRowIndicators(final Map<String, String> indicatorMap,
                                                final ListIterator<IndicatorExcelDTO> excelListIterator,
                                                final StringBuilder failureMsg) {
        try {
            final IndicatorExcelDTO indicatorRow = excelListIterator.next();
            //记录当前行的指标名称和指标单位
            getIndexParam(indicatorMap, indicatorRow);

            final String indexYear = indicatorRow.getYear();
            final String indexName = indicatorMap.get(INDEX_NAME);
            //没有年份或指标名称信息的行，不用导入
            if (StringUtils.isAnyBlank(indexYear, indexName)) {
                return Collections.emptyList();
            }

            //删除对应年份，指标名称的指标数据
            indicatorDataMapper.deleteIndicatorDataByYear(indexYear, indexName);

            //返回从导入的对象中获取实际需要的指标数据
            return getIndicatorList(new IndicatorData()
                .setIndexName(indexName)
                .setIndexUnit(indicatorMap.get(INDEX_UNIT))
                .setYear(indexYear), indicatorRow);
        } catch (final Exception e) { //不确定导入的数据在校验和转换的时候可能出现多少异常
            final String msg = String.format(ROW_ERROR_MSG, excelListIterator.nextIndex() + EXCEL_DATA_START_ROW,
                indicatorMap.get(INDEX_NAME));
            failureMsg.append(msg).append(e.getMessage());
            LOGGER.error("导入指标数据出错：错误信息：{}", msg, e);
            return Collections.emptyList();
        }
    }

    /**
     * 根据最新的一行数据，获取指标名称和指标单位.
     *
     * @param indicatorMap 指标数据
     * @param indicatorRow excel数据
     * @author helu
     * @Date 14:58 2024/4/9
     */
    private void getIndexParam(final Map<String, String> indicatorMap, final IndicatorExcelDTO indicatorRow) {
        final String indexName = StringUtils.trim(indicatorRow.getIndexName());
        final String indexUnit = StringUtils.trim(indicatorRow.getUnit());
        if (StringUtils.isNotBlank(indexName)) {
            indicatorMap.put(INDEX_NAME, indexName);
        }
        if (StringUtils.isNotBlank(indexUnit)) {
            indicatorMap.put(INDEX_UNIT, indexUnit);
        }
    }

    /**
     * 从导入对象中获取实际需要的指标数据.
     *
     * @param indicatorData 指标参数
     * @param indicatorRow  导入对象
     * @return 指标数据
     * @author helu
     * @Date 15:12 2024/4/9
     */
    private List<IndicatorData> getIndicatorList(final IndicatorData indicatorData, final IndicatorExcelDTO indicatorRow) {
        //遍历导入对象中的每一列属性,将地市属性的数据整合成需要导入的指标数据
        return BeanUtil.beanToMap(indicatorRow).entrySet().stream()
            .map(BiFunctionEnhancer.of(this::getIndicatorData).partialLeft(indicatorData))
            //只需要导入地市属性的数据
            .filter(FunctionEnhancer.with(IndicatorData::getCompanyName).thenTest(StringUtils::isNotBlank))
            .collect(Collectors.toList());
    }

    /**
     * 获取指标数据..
     *
     * @param indicatorData 指标参数
     * @param entry         指标值
     * @return net.zoneland.zrdp.biz.domain.IndicatorData
     * @author helu
     * @Date 14:30 2024/5/23
     */
    private IndicatorData getIndicatorData(final IndicatorData indicatorData, final Map.Entry<String, Object> entry) {
        //获取地市名称
        final String filedName = entry.getKey();
        final String companyName = IndicatorCityEnum.getNameByValue(filedName);

        //获取指标值
        final String indexValue = transPercent(indicatorData,
            StringUtils.trim((String) entry.getValue()), companyName);
        return new IndicatorData()
            .setCompanyName(companyName)
            .setIndexName(indicatorData.getIndexName())
            .setIndexValue(indexValue)
            .setIndexUnit(indicatorData.getIndexUnit())
            .setYear(indicatorData.getYear());
    }

    /**
     * 将指标数据转换成百分比显示..
     *
     * @param indicatorData 指标参数
     * @param value         指标值
     * @param companyName   地址名称
     * @return java.lang.String
     * @author helu
     * @Date 14:36 2024/5/23
     */
    private String transPercent(final IndicatorData indicatorData,
                                final String value,
                                final String companyName) {
        //指标值为“-"的数据一律视为空
        final String indicatorValue = StringUtils.equals(value, Constants.SPLIT_DASH) ? StringUtils.EMPTY : value;

        //部分指标必须是数字，否则抛出错误
        Preconditions.checkArgument(StringUtils.isAnyBlank(indicatorValue, companyName)
            || !CollUtil.contains(NUMBER_VALUE_INDEX_NAME, indicatorData.getIndexName())
            || checkIndicatorNumber(indicatorValue), "指标值必须是数字！当前指标值：" + value);

        //是否需要转换成百分数
        final Predicate<String> isPercent = unit -> StringUtils.equalsAny(unit, PERCENT_SIGN, PERCENT_SIGN_SPECIAL)
            && StringUtils.startsWith(value, PERCENT_START_VALUE);

        //返回正确格式的数据，普通数字保留两位小数，单位为百分号的数字转为百分数并保留两位小数，其他数据不变
        if (!NumberUtil.isNumber(indicatorValue)) {
            return indicatorValue;
        } else if (isPercent.test(indicatorData.getIndexUnit())) {
            return NumberUtil.formatPercent(NumberUtil.parseDouble(indicatorValue), DECIMALS);
        }
        return NumberUtil.decimalFormat(TWO_DECIMAL_FORMAT, NumberUtil.parseDouble(indicatorValue));
    }

    /**
     * 校验数据是否数字.
     *
     * @param value 指标值
     * @return boolean 是否是数字
     * @author helu
     * @Date 15:46 2024/5/17
     */
    private boolean checkIndicatorNumber(final String value) {
        return PERCENT_SIGNS.stream()
            .map(indicator -> StringUtils.substringBeforeLast(value, indicator))
            .anyMatch(NumberUtil::isNumber);
    }
}

